﻿/* ==============================================================================
*
*   Copyright (c) 2010, Pierre Favier
*   All rights reserved.
*
*   NTasking is free software: you can redistribute it and/or modify it
*   under the terms of the GNU General Public License as published by the
*   Free Software Foundation.
*
*   Redistribution and use in source and binary forms, with or without
*   modification, are permitted provided that the following conditions
*   are met:
*
*   -   Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*
*   -   Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials
*       provided with the distribution.
*
*   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
*   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
*   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
*   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
*   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
*   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
*   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
*   THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*   SUCH DAMAGE.
*
* ============================================================================== */

using System;
using NTasking.Internals;

namespace NTasking
{

    // ReSharper disable InconsistentNaming

    /// <summary>
    /// Define methods for support of Ada syntactic constructs which can't be implemented
    /// without a prefix
    /// </summary>
    public static partial class Ada
    {

        /// <summary>
        /// Suspend the current task for the specified number of seconds.
        /// </summary>
        /// <param name="seconds">The number of seconds.</param>
        /// <exception cref="ProgramError">
        /// Thrown if the current task is in a state where calls to
        /// potentially blocking oerations are not allowed.
        /// </exception>
        public static void Delay(double seconds)
        {
            Delay(TimeSpan.FromSeconds(seconds));
        }


        /// <summary>
        /// Suspend the current task for the specified number of milliseconds.
        /// </summary>
        /// <param name="milliseconds">The number of milliseconds.</param>
        /// <exception cref="ProgramError">
        /// Thrown if the current task is in a state where calls to
        /// potentially blocking oerations are not allowed.
        /// </exception>
        public static void Delay(int milliseconds)
        {
            Delay(TimeSpan.FromMilliseconds(milliseconds));
        }


        /// <summary>
        /// Suspend the current task for the specified time interval.
        /// </summary>
        /// <param name="interval">The time interval.</param>
        /// <exception cref="ProgramError">
        /// Thrown if the current task is in a state where calls to
        /// potentially blocking oerations are not allowed.
        /// </exception>
        public static void Delay(TimeSpan interval)
        {
            Delay(DateTime.Now + interval);
        }


        /// <summary>
        /// Suspend the current task untile the specified deadline.
        /// </summary>
        /// <param name="deadline">The deadline.</param>
        /// <exception cref="ProgramError">
        /// Thrown if the current task is in a state where calls to
        /// potentially blocking oerations are not allowed.
        /// </exception>
        public static void Delay(DateTime deadline)
        {
            // Check that this instance can be blocked since delay is an Ada
            // 'potentially blocking operation'
            if (!ThreadWrapper.CurrentThread.CanBlock)
                throw Errors.PotentiallyBlockingOperationNotAllowed("delay");

            Internals.Delay.Until(deadline);
        }


        /// <summary>
        /// Create a selective call.
        /// </summary>
        /// <param name="statements">The selective call statements.</param>
        public static void Select(Func<SelectiveCall, SelectiveCall.Complete> statements)
        {
            SelectiveCall.Select(statements);
        }


        /// <summary>
        /// Create a begin/exception/end block.
        /// </summary>
        /// <param name="begin">The begin part of the block.</param>
        /// <param name="Exception">The exception part of the block.</param>
        public static void Begin(Action begin, Func<Exception, bool> Exception = null)
        {
            new Block(null, null, begin, Exception, false);
        }


        /// <summary>
        /// Create a declare/begin/exception/end block.
        /// </summary>
        /// <param name="declare">The declare part of the block.</param>
        /// <param name="Begin">The begin part of the block.</param>
        /// <param name="Exception">The exception part of the block.</param>
        public static void Declare(Action declare, Action Begin = null, Func<Exception, bool> Exception = null)
        {
            new Block(null, declare, Begin, Exception, false);
        }


        /// <summary>
        /// Create a declare/begin/exception/end block with explicit block activation.
        /// </summary>
        /// <param name="declare">The declare part of the block.</param>
        /// <param name="Begin">The begin part of the block.</param>
        /// <param name="Exception">The exception part of the block.</param>
        internal static void Declare(Action declare, Action<Block> Begin = null, Func<Exception, bool> Exception = null)
        {
            new Block(null, declare, Begin, Exception, false);
        }


        /// <summary>
        /// Create a named declare/begin/exception/end block.
        /// </summary>
        /// <param name="name">The name of the block</param>
        /// <param name="begin">The begin part of the block.</param>
        /// <param name="Exception">The exception part of the block.</param>
        public static void Begin(this string name, Action begin, Func<Exception, bool> Exception = null)
        {
            new Block(name, null, begin, Exception, false);
        }


        /// <summary>
        /// Create a named declare/begin/exception/end block.
        /// </summary>
        /// <param name="declare">The declare part of the block.</param>
        /// <param name="name">The name of the block</param>
        /// <param name="Begin">The begin part of the block.</param>
        /// <param name="Exception">The exception part of the block.</param>
        public static void Declare(this string name, Action declare, Action Begin = null, Func<Exception, bool> Exception = null)
        {
            new Block(name, declare, Begin, Exception, false);
        }


        private static IAsynchronouslyCallable<ACallDelegateT> GetCallable<ACallDelegateT>(Delegate entry)
            where ACallDelegateT : class // Should be MulticastDelegate but prevented by compiler
        {
            if (entry == null)
                throw Errors.AsynchronousCallIsNull();

            var callable = entry.Target as IAsynchronouslyCallable<ACallDelegateT>;

            if (callable == null)
                throw Errors.AsynchronousCallNotSynchronized(entry);

            var method = callable as SynchronizedMethod;

            // ReSharper disable PossibleNullReferenceException
            if (!method.IsEntry)
                // ReSharper restore PossibleNullReferenceException
                throw Errors.AsynchronousCallNotAnEntry(entry);

            return callable;
        }


        public static int Count(this Delegate entry)
        {
            return SynchronizedObject.Count(entry);
        }

    }

}
